今天要來介紹的是 Go 的錯誤處理。
為什麼要有錯誤處理呢?原因是我們在執行程式的時候,會寫一些條件判斷,而當這些條件不符合的時候,就會需要告訴使用者或程式撰寫者,現在有錯誤出現喔!要不要檢查一下輸入的參數或是資料~
而這樣的設計,可以讓程式運行時,避免系統 crash 或發生一些無法挽回的悲劇。
所以是非常重要的呢!
Go 有兩種錯誤處理的方式:
error 是最簡單的錯誤處理方式,一般常用 if err != nil
來檢查是否有錯誤。
這部分很常用在判斷函式執行時是否有回傳錯誤,如果有,就會把錯誤印出來。我來看看以下範例:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("test.txt") // 回傳檔案和錯誤
if err != nil { // 判斷是否有回傳錯誤
fmt.Println("檔案開啟失敗:", err)
return // 遇到錯誤就結束程式或函式
}
defer file.Close()
fmt.Println("成功:", file.Name())
}
package main
import (
"fmt"
"strconv"
)
func main() {
numStr := "123a"
num, err := strconv.Atoi(numStr) // 轉成整數
if err != nil { // 判斷是否有回傳錯誤
fmt.Println("失敗:", err)
return
}
fmt.Println("成功:", num)
}
輸出:
失敗: strconv.Atoi: parsing "123a": invalid syntax
以上就是常見的錯誤處理方式, 跟其他語言使用 try
和 catch
比較不一樣~
這邊指的嚴重錯誤是 panic
,它會搭配 revocer()
一起做使用,避免程式 crash 掉。而且我們不需要主動呼叫 panic
,只要程式上做了非法操作,Go 自己會幫你呼叫 😆
那什麼情況下會導致 panic
呢? 例如:除以 0、記憶體壞了、初始化失敗等。
👉 在發生「 真的無法處理的錯誤 」的情況下,才會出現。
我們來看範例:
package main
import "fmt"
func divide(a, b int) int {
if b == 0 {
panic("division by zero!") // 除以 0 導致 panic
}
return a / b
}
func main() {
fmt.Println("開始")
result := divide(10, 0)
fmt.Println("結果:", result) // 不會執行這行程式
}
輸出:
開始
panic: division by zero!
goroutine 1 [running]:
main.divide(...)
/Users/XXXX/Desktop/To-do-List/app/main.go:7
main.main()
/Users/XXXX/Desktop/To-do-List/app/main.go:14 +0x5a
exit status 2
可以看到上面的範例,因為發生 panic,程式就直接中斷,沒有執行 fmt.Println("結果:", result)
這一行。
所以,為了讓程式可以繼續執行,我們必須加上 recover()
,才能讓程式完整的執行,避免 crash。
修改後範例:
package main
import "fmt"
func divide(a, b int) (result int) {
defer func() { // defer 會搭配 recover() 來捕捉 panic 錯誤訊息
if r := recover(); r != nil { // 判斷是否有錯誤
fmt.Println("panic:", r)
result = 0
}
}()
return a / b
}
func main() {
fmt.Println("開始")
fmt.Println("10 / 2 =", divide(10, 2))
fmt.Println("10 / 0 =", divide(10, 0))
fmt.Println("繼續執行,不會崩潰")
}
輸出:
開始
10 / 2 = 5
panic: runtime error: integer divide by zero
10 / 0 = 0
繼續執行,不會崩潰
在上面的範例程式中,可以看到它多加了 recover()
和 defer
來防止程式中斷的問題。
這兩個一定會同時出現,所以使用 recover()
時,一定要放在 defer
函式裡使用,否則會接不到錯誤。
而 recover()
適用在伺服器、goroutine,避免因為一個錯誤而導致整個服務或系統掛掉~
算是 Go 的保險機制 😆😆😆
那我們來看一下 defer
的作用:
從範例可以看到 defer func() {……}()
這個函式,它會等外層的程式執行完之後,在 return 回傳前執行。以上面的例子來看,在執行 a / b
的算式時,系統發現這個計算有問題,所以才執行 defer 裡面的程式,並返回錯誤訊息。
這個結構看起來是不是很眼熟:
if r := recover(); r != nil {
fmt.Println("panic:", r)
result = 0
}
沒錯!我們在介紹基本語法條件時,有它! 這個寫法是 if
帶初始化語句的用法,算是 Go 接收錯誤時的固定用法~ 大家可以記一下!
總結一下今天介紹的內容:
在大多數的情況下,還是使用一般的錯誤處理方式(error,if err != nil
),返回錯誤訊息。
非必要時才會使用 panic
、recover
。